HDU 4418 Time travel(高斯消元解期望dp)

题意:

$给定N\le 100长度的路,这个路是来回走的$
$比如4个点,0, 1, 2, 3, 2, 1, 0, 1, …$
$给定每次最大步数M,以及每个步数x\in[1, M]行走的概率p_x,保证\sum p_x=1$
$给定起点x,终点y,以及方向d,0正着1反着$
$求到达终点的期望步数$

分析:

$测了下数据里并没有d=-1,这傻叉题d=-1根本不知道是干嘛的,无视就行了$
$来回走的周期cycle=2n-2,所以d=1把起点x映射一下x=(cycle-x)\%cycle$
$dp状态是f[i]:=i点到达终点的期望步数$
$f[i]=\sum p_j\times (f[(i+j)\%cycle]+j)$
$之后bfs一下,求出每个点的可达性,然后对于每个点建立方程$
$注意终点有2个就行,高斯消元一波就做完了$
$时间复杂度O(n^3)$

代码:

//
//  Created by TaoSama on 2016-07-29
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 200 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

const double EPS = 1e-8;
int sgn(double x) {
    return x < -EPS ? -1 : x > EPS;
}

int n, m, y, x, d;
double p[N];

double a[N][N], ans[N];
bool isFreeX[N];

void getAns(int n, int m, int r) {
//    memset(ans, 0, sizeof ans);  //not necessary
    for(int i = r - 1; ~i; --i) {
        for(int j = 0; j < m; ++j) {
            if(!sgn(a[i][j])) continue;
            ans[j] = a[i][m];
            for(int k = j + 1; k < m; ++k)
                ans[j] -= a[i][k] * ans[k];
            ans[j] = ans[j] / a[i][j];
            break;
        }
    }
}

int gauss(int n, int m) {
    for(int i = 0; i < m; ++i) isFreeX[i] = false;
    int r = 0, c = 0;
    for(; r < n && c < m; ++r, ++c) {
        int maxR = r;       //row transform
        for(int i = r + 1; i < n; ++i)
            if(abs(a[i][c]) > abs(a[maxR][c])) maxR = i;
        if(maxR != r) swap(a[maxR], a[r]);

        if(!sgn(a[r][c])) { --r; isFreeX[c] = true; continue;}

        //eliminate coefficient
        for(int i = r + 1; i < n; ++i) {
            if(sgn(a[i][c])) {
                double delta = a[i][c] / a[r][c];
                for(int j = c; j <= m; ++j)
                    a[i][j] -= delta * a[r][j];
            }
        }
    }
    for(int i = r; i < n; i++) if(sgn(a[i][m])) return -1;

    getAns(n, m, r);

    //at last, r is rank, m - r is the number of freeX
    return r;
}

bool can[N];
bool bfs(int sx) {
    memset(can, 0, sizeof can);
    queue<int> q; q.push(sx);
    can[sx] = true;
    bool ok = false;
    while(q.size()) {
        int u = q.front(); q.pop();
        if(u == y || u == (n - y) % n) ok = true;
        for(int i = 1; i <= m; ++i) {
            if(!sgn(p[i])) continue;
            int v = (u + i) % n;
            if(can[v]) continue;
            can[v] = true;
            q.push(v);
        }
    }
    return ok;
}


int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);
    clock_t _ = clock();

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d%d%d%d", &n, &m, &y, &x, &d);
        for(int i = 1; i <= m; ++i) {
            int x; scanf("%d", &x);
            p[i] = x / 100.0;
        }
        n = 2 * n - 2; // 0,1,2,3,2,1 => 0,1,2,3,4,5

        //start coincides with destination
        if(n == 0) {puts("0.00"); continue;}

        if(d > 0) x = (n - x) % n;
        if(!bfs(x)) {puts("Impossible !"); continue;}

        memset(a, 0, sizeof a);
        //E_i = sum((E_j + j)*P_j) => E_i - sum(E_j*P_j) = sum(j*P_j)
        for(int i = 0; i < n; ++i) {
            if(i == y || i == (n - y) % n) {
                a[i][i] = 1;
                a[i][n] = 0; //E_destination = 0;
                continue;
            }
            a[i][i] = 1;
            for(int j = 1; j <= m; ++j) {
                if(!sgn(p[j])) continue;
                int x = (i + j) % n;
                if(!can[x]) continue;
                a[i][x] -= p[j];
                a[i][n] += j * p[j];
            }
        }

        gauss(n, n);
        printf("%.2f\n", ans[x]);
    }

#ifdef LOCAL
    printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
    return 0;
}